/*
 * @Author: xiaosihan
 * @Date: 2021-04-18 18:04:11
 * @Last Modified by: xiaosihan
 */

import * as React from "react";
import * as THREE from "three";
import { OrbitControls as OrbitControlsFactory } from "three/examples/jsm/controls/OrbitControls.js";
import Group3D from "../Group3D/Group3D";
import Mesh from "../Mesh/Mesh";
import Object3D from "../Object3D/Object3D";
import Object3DComponent, { Object3DComponentProps } from "../Object3D/Object3DComponent";
import { Vector3 } from "../Object3D/type";
import Transition from "../xrt_util/Transition";
import xrt_util from "../xrt_util/xrt_util";

export interface OrbitControlsProps extends Object3DComponentProps {
    initTime?: number, // 初始时间 当初始时间改变时,位置和中心点会被重置
    position?: Vector3,//------------三维坐标
    center?: Vector3,//------------旋转中心点
    autoRotate?: boolean, // 自动旋转
    autoRotateSpeed?: number, // 自动旋转的速度
    enabled?: boolean, // 启用
    enableDamping?: boolean, // 惯性
    dampingFactor?: number, // 惯性衰减速度
    rotateSpeed?: number, // 旋转速度
    enableZoom?: boolean, // 启用缩放
    zoomSpeed?: number,// 缩放速度
    minDistance?: number,// 最小距离
    maxDistance?: number, //最大距离
    maxPolarAngle?: number, // 视角不能低于水平面
    minPolarAngle?: number,  // 也不要太挨着y轴了
    maxAzimuthAngle?: number, // 最大方位角
    minAzimuthAngle?: number, // 最小方位角

    enablePan?: boolean, // 启用平移
    panSpeed?: number, // 平移速度
    screenSpacePanning?: boolean, // 屏幕空间平移
    keyPanSpeed?: number, // 按键平移速度
    enableKeys?: boolean, // 按键平移

    start?: (position: THREE.Vector3, center: THREE.Vector3) => void,// 控制位置开始改变
    change?: (position: THREE.Vector3, center: THREE.Vector3) => void,// 控制位置持续改变
    end?: (position: THREE.Vector3, center: THREE.Vector3) => void,// 控制位置结束改变
}

interface State {
    initContorls: boolean;
}

const ball = new THREE.SphereBufferGeometry(0.5, 20, 20);

const cone = new THREE.ConeBufferGeometry(1, 5, 80);

// 轨道控制器
export default class OrbitControls<P> extends Object3DComponent<P & OrbitControlsProps, State> {
    constructor(props: P & OrbitControlsProps) {
        super(props);
    }

    static defaultProps = {
        initTime: 0,
        duration: 500,
        bezier: [0.8, 0, 0.2, 1],
        position: { x: 0, y: 40, z: 50 },
        center: { x: 0, y: 0, z: 0 },
        autoRotate: false, // 自动旋转
        autoRotateSpeed: 2, // 自动旋转的速度
        enabled: true, // 启用
        enableDamping: false, // 惯性
        dampingFactor: 0.05, // 惯性衰减速度 0-1
        rotateSpeed: 0.6, // 旋转速度
        enableZoom: true, // 启用缩放
        zoomSpeed: 1,// 缩放速度
        minDistance: 0,// 最小距离
        maxDistance: Infinity, //最大距离
        maxPolarAngle: 180, // 视角不能低于水平面
        minPolarAngle: 0,  // 也不要太挨着y轴了
        maxAzimuthAngle: Infinity, // 最大方位角
        minAzimuthAngle: -Infinity,// 最小方位角
        enablePan: true, // 启用平移
        panSpeed: 1.0, // 平移速度
        screenSpacePanning: true, // 屏幕空间平移
        keyPanSpeed: 7.0, // 按键平移速度
        enableKeys: true // 按键平移
    }

    state: State = {
        initContorls: false
    }

    // 初始时间 当初始时间改变时,位置和中心点会被重置
    _initTime?: number;

    beControlCamera = new THREE.PerspectiveCamera();

    controls!: OrbitControlsFactory;

    // 创建有过渡效果的lookAt对象
    Tcenter = new Transition();

    componentBeforeRender(renderer: THREE.WebGLRenderer, scene: THREE.Scene, camera: THREE.Camera) {
        this.setContorlsPositon();
    }

    componentDidMount() {
        // 创建控制器
        this.creatContorl();

        // 修改状态为控制器初始化完成
        xrt_util.requestAnimationFrame(() => {
            this.setState({
                initContorls: true
            });
        });

        const { start, change, end, center } = this.props;

        const P = xrt_util.parseVector3(this.props.position);
        this.object3d.position.set(P.x, P.y, P.z);

        const C = xrt_util.parseVector3(center);
        this.controls.target.set(C.x, C.y, C.z);

        // 设置参数
        this.setControlsParame();

        // 设置控制器的位置
        this.setContorlsPositon();

        // 开始回调
        this.controls.addEventListener("start", () => {
            start && start(
                this.beControlCamera.position,
                this.controls.target
            );
        });

        // 改变时回调
        this.controls.addEventListener("change", (e) => {

            this.Tposition.reset({
                x: this.beControlCamera.position.x,
                y: this.beControlCamera.position.y,
                z: this.beControlCamera.position.z
            });

            this.Tcenter.reset({
                x: this.controls.target.x,
                y: this.controls.target.y,
                z: this.controls.target.z
            });

            this.object3d.position.copy(this.beControlCamera.position);
            this.object3d.rotation.copy(this.beControlCamera.rotation);

            change && change(
                this.beControlCamera.position,
                this.controls.target
            );
        });

        // 结束回调
        this.controls.addEventListener("end", () => {
            end && end(
                this.beControlCamera.position,
                this.controls.target
            );
        });
    }


    // 创建控制器
    creatContorl() {
        const { renderer } = this.context;
        this.controls = new OrbitControlsFactory(this.beControlCamera, renderer.domElement); //轨道控制器
    }

    componentDidUpdate() {
        // 设置参数
        this.setControlsParame();
    }

    // 不执行父类的此方法
    setTansformParam() {
    }

    // 不执行父类的此方法
    setObjectParam() {
        return false;
    }

    componentWillUnmount() {
        this.controls.dispose();
    }

    // 设置参数
    setControlsParame() {

        const {
            initTime = 0,
            duration = 500,
            bezier = [0.8, 0, 0.2, 1],
            center,
            position,
            autoRotate = false, // 自动旋转
            autoRotateSpeed = 2, // 自动旋转的速度
            enabled = true, // 启用
            enableDamping = false, // 惯性
            dampingFactor = 0.05, // 惯性衰减速度
            rotateSpeed = 0.6, // 旋转速度
            enableZoom = false, // 禁用缩放
            zoomSpeed = 1,// 缩放速度
            minDistance = 0,// 最小距离
            maxDistance = Infinity, //最大距离
            maxPolarAngle = 180, // 视角不能低于水平面
            minPolarAngle = 0,  // 也不要太挨着y轴了
            maxAzimuthAngle = Infinity, // 最大方位角
            minAzimuthAngle = -Infinity, // 最小方位角

            enablePan = true, // 启用平移
            panSpeed = 1.0, // 平移速度
            screenSpacePanning = true, // 屏幕空间平移
            keyPanSpeed = 7.0, // 按键平移速度
            enableKeys = true // 按键平移
        } = this.props;

        if (this._initTime !== initTime) {

            this._initTime = initTime;

            let currentPosition = this.object3d.position.clone();
            let currentCenter = this.controls.target.clone();

            //重置控制器位置时 设置阻力最大, 惯性为false
            this.controls.dampingFactor = 0;
            this.controls.enableDamping = false;
            this.controls.autoRotate = false;
            this.controls.reset();

            this.beControlCamera.position.copy(currentPosition);
            this.beControlCamera.lookAt(currentCenter);

            this.object3d.position.copy(this.beControlCamera.position);
            this.object3d.rotation.copy(this.beControlCamera.rotation);

            this.object3d.updateMatrix();
            this.object3d.updateMatrixWorld(true);

            // 位置
            const P = xrt_util.parseVector3(position);
            this.Tposition
                .setBezier(bezier)
                .setDuration(duration)
                .reset({
                    x: currentPosition.x,
                    y: currentPosition.y,
                    z: currentPosition.z
                })
                .set({ x: P.x, y: P.y, z: P.z });

            // 中心点
            const C = xrt_util.parseVector3(center);
            this.Tcenter
                .setBezier(bezier)
                .setDuration(duration)
                .reset({
                    x: currentCenter.x,
                    y: currentCenter.y,
                    z: currentCenter.z
                })
                .set({ x: C.x, y: C.y, z: C.z })
        }


        Object.assign(this.controls, {
            enableDamping,
            dampingFactor,
            autoRotate,
            enabled,
            autoRotateSpeed,
            rotateSpeed,
            enableZoom,
            zoomSpeed,
            minDistance,
            maxDistance,
            maxPolarAngle,
            minPolarAngle,
            maxAzimuthAngle,
            minAzimuthAngle,
            enablePan,
            panSpeed,
            screenSpacePanning,
            keyPanSpeed,
            enableKeys
        });

    }

    setContorlsPositon() {
        const { enableDamping, autoRotate } = this.props;

        // 修改位置
        if (!this.Tposition.isDone() || !this.Tcenter.isDone()) {
            const { x: px, y: py, z: pz } = this.Tposition.getJson();
            this.beControlCamera.position.set(px, py, pz);


            const { x: cx, y: cy, z: cz } = this.Tcenter.getJson();
            this.controls.target.set(cx, cy, cz);

            this.beControlCamera.lookAt(this.controls.target);

            this.object3d.position.copy(this.beControlCamera.position);
            this.object3d.rotation.copy(this.beControlCamera.rotation);

        } else {

            //惯性和旋转角度开启时需要时时更新
            if (enableDamping === true || autoRotate === true) {
                this.controls.update();
            }
        }

        this.object3d.updateMatrix();
    }

    getTitleText() {
        return (
            <>
                <span>{"轨道控制器"}<br /></span>
                <span>{"<OrbitControls/>"}<br /></span>
            </>
        )
    }

    render() {

        const { dev } = this.context;
        const { initContorls } = this.state;

        return (
            <>
                {this.props.children}
                {
                    dev &&
                    <>
                        <Mesh
                            geometry={cone}
                            material="MeshNormalMaterial"
                            clipping={false}
                            color="#ccc"
                            opacity={0.5}
                            renderOrder={1}
                            rotation={{ x: Math.PI / -90 }}
                            position={{ z: 3 }}
                            side={THREE.FrontSide}
                        />
                        {
                            initContorls &&
                            <Group3D
                                lookAt="camera"
                                position={(renderer: THREE.Renderer, scene: THREE.Scene, camera: THREE.Camera, object3d: THREE.Object3D) => {
                                    if (this.controls && object3d.parent) {
                                        return { z: -object3d.parent.position.distanceTo(this.controls.target) };
                                    }
                                    return {};

                                }}
                            >
                                <Mesh
                                    geometry={ball}
                                    opacity={0.5}
                                    side={THREE.BackSide}
                                    material="MeshNormalMaterial"
                                    clipping={false}
                                    color="#fe3aff"
                                    cursor="pointer"
                                    renderOrder={1}
                                    title={
                                        <div
                                            style={{
                                                position: "relative",
                                                backgroundColor: "rgba(0, 0, 0, 0.8)",
                                                borderRadius: 4,
                                                padding: "4px 8px",
                                                color: "#fff"
                                            }}
                                        >
                                            {this.getTitleText()}
                                        </div>
                                    }
                                />
                            </Group3D>
                        }
                    </>
                }
            </>
        );
    }
}